package br.com.sovi.gwt.client.controleunitario;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import br.com.sovi.gwt.client.entity.ControleUnitario;
import br.com.sovi.gwt.client.entity.ControleUnitarioPoint;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.maps.client.MapType;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.control.LargeMapControl3D;
import com.google.gwt.maps.client.geocode.DirectionQueryOptions;
import com.google.gwt.maps.client.geocode.DirectionResults;
import com.google.gwt.maps.client.geocode.Directions;
import com.google.gwt.maps.client.geocode.DirectionsCallback;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.LocationCallback;
import com.google.gwt.maps.client.geocode.Placemark;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.PolyStyleOptions;
import com.google.gwt.maps.client.overlay.Polyline;
import com.google.gwt.user.client.Timer;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.layout.FitLayout;

public class ControleUnitarioCentralPanel extends Panel {

	private MapWidget map;

	private List<ControleUnitarioPoint> dots;

	private Map<ControleUnitarioPoint, JsArray<Placemark>> locationsMap;

	public ControleUnitarioCentralPanel() {
		this.setLayout(new FitLayout());

		map = new MapWidget();
		map.setSize("100%", "100%");
		map.addControl(new LargeMapControl3D());

		new Timer() {
			@Override
			public void run() {
				map.checkResizeAndCenter();
			}
		}.schedule(1);

		this.add(map);
		this.doLayout();
	}

	public void populateScreen(final ControleUnitario controleUnitario) {
		map.clearOverlays();

		List<ControleUnitarioPoint> points = controleUnitario.getPoints();

		if (points == null || points.size() <= 0) {
			GWT.log("Nenhum ponto encontrado para ser mostrado.", null);
			return;
		}

		boolean firstCenter = true; // para centralizar apenas o primeiro

		for (int indexPoint = 0; indexPoint < points.size(); indexPoint++) {
			final ControleUnitarioPoint point = points.get(indexPoint);

			dots = new ArrayList<ControleUnitarioPoint>();

			final LatLng latLng = LatLng.newInstance(point.getPoint().getLatitude(), point.getPoint().getLongitude());

			map.addOverlay(new Marker(latLng));
			this.add(map);

			if (firstCenter) {
				map.setCenter(latLng, 13);
				firstCenter = false;
			}

			new Geocoder().getLocations(latLng, new LocationCallback() {
				@Override
				public void onSuccess(JsArray<Placemark> locations) {
					GWT.log("Recebendo as locations...", null);
					locationsMap = new HashMap<ControleUnitarioPoint, JsArray<Placemark>>();
					locationsMap.put(point, locations);
					dots.add(point);
				}

				@Override
				public void onFailure(int statusCode) {
					GWT.log("Falha no geocoding: " + statusCode, null);
					dots.add(point);
				}
			});

		}

		if (controleUnitario.isTrafego()) {
			map.setCurrentMapType(MapType.getNormalMap());
			createPolylines();
		}
		else {
			map.setCurrentMapType(MapType.getSatelliteMap());

			if (controleUnitario.isRastro()) {
				createRastro(controleUnitario);
			}
		}

		if (controleUnitario.getZoom() > 0) {
			map.setZoomLevel(controleUnitario.getZoom());
		}

		this.doLayout();
	}

	private void createRastro(ControleUnitario controleUnitario) {
		List<ControleUnitarioPoint> points = controleUnitario.getPoints();

		LatLng[] lls = new LatLng[points.size()];

		for (int i = 0; i < points.size(); i++) {
			ControleUnitarioPoint controleUnitarioPoint = points.get(i);
			lls[i] = LatLng.newInstance(controleUnitarioPoint.getPoint().getLatitude(), controleUnitarioPoint
					.getPoint().getLongitude());
		}

		Polyline p = new Polyline(lls);

		PolyStyleOptions instance = PolyStyleOptions.getInstance();
		instance.setColor("red");
		instance.setOpacity(30);

		p.setStrokeStyle(instance);
		map.addOverlay(p);
	}

	private void createPolylines() {
		new Timer() {
			@Override
			public void run() {
				GWT.log("Iniciando recurso de polyline: " + dots.size(), null);

				DirectionQueryOptions directionQueryOptions = new DirectionQueryOptions();
				directionQueryOptions.setRetrievePolyline(true);

				for (int i = 0; i < dots.size(); i++) {
					ControleUnitarioPoint currentEntry = dots.get(i);
					ControleUnitarioPoint nextEntry = null;

					try {
						nextEntry = dots.get((i + 1));
					}
					catch (Exception e) {
						continue; // Se não existe próximo elemento, é só
						// finalizar o loop
					}

					if (locationsMap != null) {
						JsArray<Placemark> currentArray = locationsMap.get(currentEntry);
						JsArray<Placemark> nextArray = locationsMap.get(nextEntry);

						if (currentArray == null && nextArray == null) {
							GWT.log("Alguma Location não existe!", null);
							continue;
						}
					}

					// criar as directions na tela.
					Directions.load(createDirectionQuery(currentEntry, nextEntry), directionQueryOptions,
							new DirectionsCallback() {
								@Override
								public void onSuccess(DirectionResults result) {
									GWT.log("Recebendo resultados.", null);
									map.addOverlay(result.getPolyline());
								}

								@Override
								public void onFailure(int statusCode) {
									GWT.log("Erro ao capturar directions: " + statusCode, null);
								}
							});
				}
			}

			private String createDirectionQuery(ControleUnitarioPoint currentEntry, ControleUnitarioPoint nextEntry) {

				if (locationsMap != null) {
					StringBuilder sb = new StringBuilder("from: ");

					JsArray<Placemark> currentArray = locationsMap.get(currentEntry);
					JsArray<Placemark> nextArray = locationsMap.get(nextEntry);

					sb.append(currentArray.get(0).getAddress());
					sb.append(" to:");
					sb.append(nextArray.get(0).getAddress());

					return sb.toString();
				}

				return "";
			}

		}.schedule(2500);
	}

}
